package org.example.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT token 相关操作
 * @author Administrator
 *
 */
@Slf4j
public class AuthControl {


	/**
	 * token key,用于加密与解密token的key，请勿随意更改泄露
	 */
    public static final String SECRET = "ZRDXmingcedashuju";
    /**
     * 邮箱期计算格式，以 Calendar 参数为准 默认分钟
     */
    public static final int calendarField = Calendar.MINUTE;
    /**
     * 设置token过期时间，以 Calendar 参数为准 默认分钟
     */
    public static final int calendarInterval = 30;
    /**
     * 设置黑名单存放时间，基本上设置与 calendarInterval 同步，单位默认分钟
     */
    public static final int blacklistTime = 60*24;
    
    /**
     * token 自动更新限制时间，单位分钟，有效期剩余三分钟后的请求自动更新token
     */
    public static final long refreshTime = 30;
    
    /**
     * 获取指定用户id的权限认定token
     * @param userId
     * @return String
     * @throws Exception
     */
    public static String createToken(Long userId) throws Exception {
    	// 日期范围参数
        Date iatDate = new Date();
        Calendar nowTime = Calendar.getInstance();
        nowTime.add(calendarField, calendarInterval);
        Date expiresDate = nowTime.getTime();

        // 设置加密方式 ：HS256 HS384 HS512 RS256等
        Map<String, Object> map = new HashMap<>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");

        // 整合token
        String token = JWT.create().withHeader(map)
                .withClaim("iss", "Service")
                .withClaim("aud", "APP")
                .withClaim("time", String.valueOf(expiresDate.getTime()))
                .withClaim("userId", null == userId ? null : userId.toString())
                .withIssuedAt(iatDate)
                .withExpiresAt(expiresDate)
                .sign(Algorithm.HMAC256(SECRET));
        return token;
    }


    /**
     * 获取指定用户id的权限认定token
     *
     * @param userId
     * @return String
     * @throws Exception
     */
    public static String createToken(String userId) throws Exception {
        // 日期范围参数
        Date iatDate = new Date();
        Calendar nowTime = Calendar.getInstance();
        nowTime.add(calendarField, calendarInterval);
        Date expiresDate = nowTime.getTime();

        // 设置加密方式 ：HS256 HS384 HS512 RS256等
        Map<String, Object> map = new HashMap<>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");

        // 整合token
        String token = JWT.create().withHeader(map)
                .withClaim("iss", "Service")
                .withClaim("aud", "APP")
                .withClaim("time", String.valueOf(expiresDate.getTime()))
                .withClaim("userId", null == userId ? null : userId)
                .withIssuedAt(iatDate)
                .withExpiresAt(expiresDate)
                .sign(Algorithm.HMAC256(SECRET));
        return token;
    }
    
    
    /**
     * token 解密
     * 特注:解密与加密 的token key 必须一致
     * 
     * @param token
     * @return
     */
    public static Map<String, Claim> verifyToken(String token) throws Exception {
        if(token == null) return null;
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        JWTVerifier verifier = JWT.require(algorithm).build();
        DecodedJWT jwt = verifier.verify(token);
        return jwt.getClaims();
    }
    
    /**
     * 根据Token获取userId
     * 
     * @param token
     * @return userId
     * @throws Exception 
     */
    public static Long getUserId(String token){
        Map<String, Claim> claims = null;
        try {
            claims = verifyToken(token);
            Claim userId_claim = claims.get("userId");
            if (null == userId_claim || userId_claim.asString() == null) {
                return  null;
            }
            return Long.valueOf(userId_claim.asString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0L;
    }
    
    /**
     * 根据Token获取剩余结束时间
     * 单位毫秒
     * 
     * @param token
     * @return time
     * @throws Exception 
     */
    public static Long getFinishTime(String token) throws Exception {
    	Map<String, Claim> claims = verifyToken(token);
    	long exp = new Long(claims.get("time").asString());
        long now = new Date().getTime();
        long among = exp - now;
        return among;
    }

    /**
     * 根据Token获取过期时间
     * 单位毫秒
     *
     * @param token
     * @return time
     * @throws Exception
     */

    public static Long getFinishDateTime(String token) throws Exception {
        Map<String, Claim> claims = verifyToken(token);
        long exp = new Long(claims.get("time").asString());
        return exp;
    }

    /**
     * 刷新token 将传入的token 进行刷新
     * 特注：token 一旦执行刷新过后将失效
     *
     * @param token 限制在快过期时间内的，如果尚未过期范围则返回  null
     * @return token 新的token
     * @throws Exception
     */

    public static String getRefresh(String token) {
        try {
            long among = getFinishTime(token);
            long astrict = refreshTime*60*1000;
            if(among > astrict) {
                return null;
            }
            //将当前token 加入黑名单
            setBlacklist(token);
            return createToken(getUserId(token));
        }catch (Exception e){
            return null;
        }
    }

    /**
     * 刷新token 将传入的token 进行刷新
     * 特注： 该函数不进行时间处理 重载方法
     * 
     * @param token
     * @param direct 初始token是否加入黑名单
     * @return token 新的token
     * @throws Exception 
     */
    public static String getRefresh(String token,boolean direct) throws Exception {
        if(direct){
            setBlacklist(token);
        }
    	return createToken(getUserId(token));
    }
    
    /**
     * 将指定token 拉入黑名单
     * 特注：黑名单 缓存使用 redis 
     * 
     * @param token
     * @return
     * @throws Exception 
     */
    public static boolean setBlacklist(String token) throws Exception {
    	long astrict = blacklistTime*60*1000;
        Jedis JedisObj = RedisPond.getJedis();
        JedisObj.set(token,String.valueOf(
    			getUserId(token))
    			,"NX"
    			,"PX"
    			,astrict);
    	RedisPond.returnResource(JedisObj);
    	return true;
    }
    
    /**
     * 判断token 是否存在于黑名单
     * 
     * @param token
     * @return false 不存在， true 存在
     */
    public static boolean judgeToken(String token) {
        Jedis JedisObj = RedisPond.getJedis();
    	String result = JedisObj.get(token);
    	RedisPond.returnResource(JedisObj);
    	if(result == null) {
    		
    		return false;
    	}
    	
    	return true;
    }
    
    /**
     * 判断token 是否有效
     * 特注：就是不能在黑名单以及无效token
     * 
     * @param token
     * @return false 无效  true 有效
     * @throws Exception 
     */
    public static boolean tokenIsExist(String token) {
		try {
			if(judgeToken(token)) {
	    		return false;
	    	}
	    	Long uid;
			uid = getUserId(token);
			if(uid == null || uid == 0) {
	    		return false;
	    	}
		} catch (Exception e) {
			// TODO 自动生成的 catch 块
			return false;
		}
    	return true;



    }
    
    
}
